# -*- coding: UTF-8 -*-
# 使用6.6.1版本的API
import sys
import time
from threading import Thread

import Function
import GlobalParam
from LogUtil import timePrintLog, red_printLog, timePrint
from Function import judge_ret, get_tick

if 'linux' in sys.platform:
    from CTP_API.linux import thostmduserapi as mdapi
if 'win' in sys.platform:
    from CTP_API.window import thostmduserapi as mdapi


# CTP行情接口
class CFtdcMdSpi(mdapi.CThostFtdcMdSpi):

    def __init__(self, exchange, mduserapi):
        mdapi.CThostFtdcMdSpi.__init__(self)
        self.mduserapi = mduserapi
        self.status = False
        self.exchange = exchange

    def connect(self):
        try:
            self.mduserapi = mdapi.CThostFtdcMdApi_CreateFtdcMdApi('./md/con_file/')
            # 创建spi实例
            self.mduserspi = CFtdcMdSpi(self.exchange, self.mduserapi)
            # 连接行情前置服务器
            self.mduserapi.RegisterFront(GlobalParam.market_server_front)
            self.mduserapi.RegisterFront(GlobalParam.market_server_front1)
            self.mduserapi.RegisterFront(GlobalParam.market_server_front2)
            self.mduserapi.RegisterFront(GlobalParam.market_server_front3)
            self.mduserapi.RegisterFront(GlobalParam.market_server_front4)
            self.mduserapi.RegisterFront(GlobalParam.market_server_front5)
            # 将spi注册给api
            self.mduserapi.RegisterSpi(self.mduserspi)
            # 第5步，API正式启动，dll底层会自动去连上面注册的地址
            self.mduserapi.Init()
            # join的目的是为了阻塞主线程
            self.mduserapi.Join()
        except Exception as e:
            print(e.__str__())

    # 当客户端与交易后台建立起通信连接时（还未登录前），该方法被调用
    def OnFrontConnected(self):
        timePrintLog(str(self.exchange)+"开始建立行情连接")
        # 发送用户登录请求
        loginfield = mdapi.CThostFtdcReqUserLoginField()
        loginfield.BrokerID = GlobalParam.broker_id
        loginfield.UserID = GlobalParam.investorID
        loginfield.Password = GlobalParam.password

        ret = self.mduserapi.ReqUserLogin(loginfield, 0)

        if ret == 0:
            timePrintLog(str(self.exchange)+'发送用户登录行情账户请求成功！')
        else:
            timePrintLog(str(self.exchange)+'发送用户登录行情账户请求失败！')
            judge_ret(ret)

    def OnFrontDisconnected(self, nReason):
        timePrintLog(str(self.exchange)+"的行情连接断开,原因："+str(nReason))

    def OnRspError(self, pRspInfo, nRequestID, bIsLast):
        timePrintLog(str(self.exchange) + "错误信息：" + str(pRspInfo))

    def OnRspUserLogin(self, pRspUserLogin, pRspInfo, nRequestID, bIsLast):
        if pRspInfo.ErrorID != 0 and pRspInfo != None:
            timePrintLog(str(self.exchange)+'行情连接失败\n错误信息为：{}\n错误代码为：{}'.format(pRspInfo.ErrorMsg, pRspInfo.ErrorID))
        else:
            timePrintLog(str(self.exchange)+'行情账户登录成功！')
            if not self.status:
                self.subscribeMarketData()
                t0 = Thread(target=get_tick)
                t0.start()
            else:
                timePrintLog(str(self.exchange) + '行情断开连接！')

            self.status = True

    # 订阅行情应答函数
    def OnRspSubMarketData(self, pSpecificInstrument, pRspInfo, nRequestID, bIsLast):
        if pRspInfo.ErrorID != 0 and pRspInfo != None:
            timePrintLog(
                str(self.exchange)+'订阅行情失败！\n订阅代码：{}\n错误信息为：{}\n错误代码为：{}'.format(pSpecificInstrument.InstrumentID,
                                                                                  pRspInfo.ErrorMsg,
                                                                                  pRspInfo.ErrorID))
        else:
            timePrintLog(str(self.exchange)+'订阅{}行情成功！'.format(pSpecificInstrument.InstrumentID))

    # 深度行情通知
    def OnRtnDepthMarketData(self, pDepthMarketData):
        try:
            tikerMarketData = {'TradingDay': pDepthMarketData.TradingDay,
                               'InstrumentID': pDepthMarketData.InstrumentID,
                               'LastPrice': round(pDepthMarketData.LastPrice, 4),
                               'BidPrice1': pDepthMarketData.BidPrice1,
                               'AskPrice1': pDepthMarketData.AskPrice1,
                               'PreSettlementPrice': pDepthMarketData.PreSettlementPrice,
                               'PreClosePrice': pDepthMarketData.PreClosePrice,
                               'SettlementPrice': pDepthMarketData.SettlementPrice,
                               'ClosePrice': pDepthMarketData.ClosePrice,
                               'UpperLimitPrice': round(pDepthMarketData.UpperLimitPrice, 4),
                               'LowerLimitPrice': round(pDepthMarketData.LowerLimitPrice, 4),
                               'UpdateTime': pDepthMarketData.UpdateTime}

            GlobalParam.tickQueue.put(tikerMarketData)
        except Exception as e:
            red_printLog(e)
            return

    # 订阅全部行情
    def subscribeMarketData(self):
        try:
            timePrint(str(self.exchange) + '开始订阅行情')
            sub_instrument = self.load()
            for i in range(0, len(sub_instrument), 10):
                if i + 10 <= len(sub_instrument):
                    self.sub(sub_instrument[i:i + 10])
                else:
                    self.sub(sub_instrument[i:])
                time.sleep(0.1)

            timePrint(str(self.exchange) + '已发送全部期权合约订阅请求')
        except Exception as e:
             print(e.__str__())

    # 订阅少量行情
    def sub(self, subID_list):
        ret = self.mduserapi.SubscribeMarketData([id.encode('utf-8') for id in subID_list], len(subID_list))
        if ret == 0:
            timePrintLog(str(self.exchange)+'发送订阅{}合约请求成功！'.format(str(subID_list)))
        else:
            timePrintLog(str(self.exchange)+'发送订阅{}合约请求失败！'.format(str(subID_list)))
            judge_ret(ret)
            while ret != 0:
                ret = self.mduserapi.SubscribeMarketData([id.encode('utf-8') for id in subID_list], len(subID_list))
                timePrintLog(str(self.exchange)+'正在订阅{}行情...'.format(str(subID_list)))
                time.sleep(1)

        # 加载需要订阅的合约


    def load(self):
        try:
            instrument_all = Function.instrument_info_all
            # 创建所有合约对应的键值对
            instrument = list(instrument_all.keys())
            # 如果只订阅期货加下边这句
            instrument = [i for i in instrument if instrument_all[i] == self.exchange]

            if GlobalParam.future_type == 'future':
                # 订阅期货
                instrument = [s for s in instrument if len(s) <= 6]

            elif GlobalParam.future_type == 'option':
                # 订阅期权
                instrument = [s for s in instrument if len(s) > 6]

            return instrument
        except Exception as e:
            print(e.__str__())

def ConnectMd_CFFEX():
    md_future = CFtdcMdSpi(exchange='CFFEX', mduserapi=None)
    md_future.connect()
def ConnectMd_CZCE():
    md_future = CFtdcMdSpi(exchange='CZCE', mduserapi=None)
    md_future.connect()
def ConnectMd_DCE():
    md_future = CFtdcMdSpi(exchange='DCE', mduserapi=None)
    md_future.connect()
def ConnectMd_GFEX():
    md_future = CFtdcMdSpi(exchange='GFEX', mduserapi=None)
    md_future.connect()
def ConnectMd_INE():
    md_future = CFtdcMdSpi(exchange='INE', mduserapi=None)
    md_future.connect()
def ConnectMd_SHFE():
    md_future = CFtdcMdSpi(exchange='SHFE', mduserapi=None)
    md_future.connect()


